package com.youdoneed.friends.coolface;

import com.querydsl.sql.SQLQueryFactory;
import com.querydsl.sql.dml.SQLUpdateClause;
import com.youdoneed.base.application.AppException;
import com.youdoneed.base.application.ResultMessage;
import com.youdoneed.base.common.FileOperation;
import com.youdoneed.base.common.GlobalIdWorker;
import com.youdoneed.base.common.GsonUtils;
import com.youdoneed.base.common.JsonConverter;
import com.youdoneed.base.common.OkHttpUtils;
import com.youdoneed.base.common.TxtUtil;
import com.youdoneed.friends.application.AppConfig;
import com.youdoneed.friends.friends.FriendsService;
import com.youdoneed.friends.lostChild.LostChildService;
import com.youdoneed.friends.model.FaceSearch;
import com.youdoneed.friends.model.LostChild;
import com.youdoneed.friends.query.QFaceSearch;
import com.youdoneed.friends.query.QLostChild;
import com.youdoneed.friends.vo.AppSearchResult;
import com.youdoneed.friends.vo.FaceSearchChild;
import com.youdoneed.friends.vo.FaceSearchUser;
import com.youdoneed.friends.vo.ImgComparisonResult;
import com.youdoneed.news.feign.NewsClient;
import com.youdoneed.news.model.CmfPostsListVo;
import com.youdoneed.news.vo.FaceSearchNews;
import com.youdoneed.user.feign.UserFeignClient;
import com.youdoneed.user.model.User;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.util.*;

@Service
@Transactional
public class FaceSearchService {

    private final Logger logger = LoggerFactory.getLogger(FaceSearchService.class);

    @Autowired
    private AppConfig appConfig;
    @Autowired
    private SQLQueryFactory queryFactory;
    private QFaceSearch qFaceSearch = QFaceSearch.FaceSearch;
    private QLostChild qLostChild = QLostChild.LostChild;

    @Autowired
    private FriendsService friendsService;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private HasCheckCoolFaceService hasCheckCoolFaceService;

    @Autowired
    private LostChildService lostChildService;

    @Autowired
    private NewsClient newsClient;

    public FaceSearch getFaceSearchBySearchId(String searchId) {
        if (!StringUtils.hasText(searchId)) {
            throw new AppException("searchId为空!");
        }

        try {
            return queryFactory.selectFrom(qFaceSearch)
                    .where(qFaceSearch.searchId.eq(searchId))
                    .fetchOne();
        } catch (Exception e) {
            logger.info(e.getMessage());
            return null;
        }
    }

    public ImgComparisonResult imgComparison(String newPathName, User myInfo) {
        if (!StringUtils.hasText(newPathName) || !StringUtils.hasText(myInfo.getImageUrl())) {
            throw new AppException("比对的两张图片都不能为空。");
        }
        // 准备搜索参数并比对图片
        FaceSearchParam param = new FaceSearchParam();
        param.msg_type = "imgComparison";
        try {
            byte[] bytes = OkHttpUtils.get(newPathName).callForBytes();
            ImageData imageData = new ImageData();
            imageData.image = Base64.getEncoder().encodeToString(bytes);
            imageData.md5 = DigestUtils.md5DigestAsHex(bytes);
            param.content.add(imageData);
            byte[] bytes2 = OkHttpUtils.get(myInfo.getImageUrl()).callForBytes();
            ImageData imageData2 = new ImageData();
            imageData2.image = Base64.getEncoder().encodeToString(bytes2);
            imageData2.md5 = DigestUtils.md5DigestAsHex(bytes2);
            param.content.add(imageData2);
        } catch (Exception e) {
            throw new AppException("获取图片数据失败。");
        }
        ImgComparisonResult result = OkHttpUtils.formPost(appConfig.getFaceRecognitionUrl())
                .param("data", GsonUtils.toJson(param))
                .callForObject(ImgComparisonResult.class);

        logger.info("result:" + JsonConverter.toJson(result));

        if (result == null || !"1".equals(result.getErrcode())) {
            throw new AppException("图像比对失败。").setData(result);
        }
        return result;
    }

    public boolean isSameUser(String newPath, String oldPath) {
        if (!StringUtils.hasText(newPath) || !StringUtils.hasText(oldPath)) {
            throw new AppException("比对的两张图片都不能为空。");
        }
        // 准备搜索参数并比对图片
        FaceSearchParam param = new FaceSearchParam();
        param.msg_type = "imgComparison";
        try {
            byte[] bytes = OkHttpUtils.get(newPath).callForBytes();
            ImageData imageData = new ImageData();
            imageData.image = Base64.getEncoder().encodeToString(bytes);
            imageData.md5 = DigestUtils.md5DigestAsHex(bytes);
            param.content.add(imageData);
            byte[] bytes2 = OkHttpUtils.get(oldPath).callForBytes();
            ImageData imageData2 = new ImageData();
            imageData2.image = Base64.getEncoder().encodeToString(bytes2);
            imageData2.md5 = DigestUtils.md5DigestAsHex(bytes2);
            param.content.add(imageData2);
        } catch (Exception e) {
            throw new AppException("获取图片数据失败。");
        }
        ImgComparisonResult result = OkHttpUtils.formPost(appConfig.getFaceRecognitionUrl())
                .param("data", GsonUtils.toJson(param))
                .callForObject(ImgComparisonResult.class);

        logger.info("result:" + JsonConverter.toJson(result));

        if (result == null || !"1".equals(result.getErrcode())) {
            throw new AppException("图像比对失败。").setData(result);
        }
        String scoreStr = result.getScore();
        if (!StringUtils.hasText(scoreStr)) {
            throw new AppException("图像比对失败。").setData(result);
        }

        Long score = Long.valueOf(scoreStr);
        if (score - 8000 > 0) {
            return true;
        }
        return false;
    }

    public ImgComparisonResult genImgFeature(String pathName, String userId) {
        // 准备搜索参数并提取人脸特征
        FaceSearchParam param = new FaceSearchParam();
        param.msg_type = "genImgFeature";
        // 0.搜索明星，1.搜索用户
        param.type = "1";
        param.userId = userId;
        try {
            byte[] bytes = OkHttpUtils.get(pathName).callForBytes();
            ImageData imageData = new ImageData();
            imageData.image = Base64.getEncoder().encodeToString(bytes);
            imageData.md5 = DigestUtils.md5DigestAsHex(bytes);
            param.content.add(imageData);
        } catch (Exception e) {
            throw new AppException("获取图片数据失败。");
        }
        ImgComparisonResult result = OkHttpUtils.formPost(appConfig.getFaceRecognitionUrl())
                .param("data", GsonUtils.toJson(param))
                .callForObject(ImgComparisonResult.class);
        if (result == null || !"1".equals(result.getErrcode())) {
            throw new AppException("人脸特征提取失败。").setData(result);
        }
        return result;
    }


    /**
     * 批量插入儿童的特征值（临时方法）
     *
     * @author lid
     * @date 2017.12.19
     */
    public void getChildImgFeatureInBatch() {
        ResultMessage msg = userFeignClient.getAllUser();
        List<User> userList = new ArrayList<>();
        if (msg.isSuccess()) {
            userList = (List<User>) msg.getData();
        }


        logger.info("userList.size():" + userList.size());
        for (int i = 0; i < userList.size(); i++) {
            User user = userList.get(i);
            logger.info("开始第" + i + "个特征值的入库");
            try {
                this.genImgFeatureByType(user.getImageUrl(), user.getUserId(), "1");
            } catch (Exception e) {
                logger.error(e.getMessage());
            }

            logger.info("完成第" + i + "个特征值的入库");
        }
    }

    public ImgComparisonResult genImgFeatureByType(String pathName, String userId, String type) {

        File copyFile = new File("d:/upload/" + userId + ".txt");
        String content = pathName;
        if (!StringUtils.hasText(pathName)) {
            content += "图片链接为空";
            FileOperation.createFile(copyFile);
            try {
                FileOperation.writeTxtFile(content, copyFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
//            throw new AppException("图片链接为空");
        }

        if (!StringUtils.hasText(userId)) {
            content += "userId为空";
            FileOperation.createFile(copyFile);
            try {
                FileOperation.writeTxtFile(content, copyFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
            throw new AppException("userId为空");
        }

        if (!StringUtils.hasText(type)) {
            type = "1";
        }

        // 准备搜索参数并提取人脸特征
        FaceSearchParam param = new FaceSearchParam();
        param.msg_type = "genImgFeature";
        // 0.搜索明星，1.搜索用户
        param.type = type;
        param.userId = userId;
        try {
            byte[] bytes = OkHttpUtils.get(pathName).callForBytes();
            ImageData imageData = new ImageData();
            imageData.image = Base64.getEncoder().encodeToString(bytes);
            imageData.md5 = DigestUtils.md5DigestAsHex(bytes);
            param.content.add(imageData);
        } catch (Exception e) {
            content += "获取图片数据失败";
            FileOperation.createFile(copyFile);
            try {
                FileOperation.writeTxtFile(content, copyFile);
            } catch (IOException e2) {
                e2.printStackTrace();
            }
            throw new AppException("获取图片数据失败。");
        }
        ImgComparisonResult result = OkHttpUtils.formPost(appConfig.getFaceRecognitionUrl())
                .param("data", GsonUtils.toJson(param))
                .callForObject(ImgComparisonResult.class);
        try {
            if (result == null || !"1".equals(result.getErrcode())) {
                logger.error("人脸特征提取失败。" + userId);
                content += result.toString();
                FileOperation.createFile(copyFile);
                FileOperation.writeTxtFile(content, copyFile);

//            throw new AppException("人脸特征提取失败。").setData(result);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 更新搜索记录（留言等）
     */
    public long updateSearch(String searchId, String msg,
                             Double longitude, Double latitude, String place) {
        if (!StringUtils.hasText(searchId)) {
            throw new AppException("搜索ID不能为空！");
        }
        SQLUpdateClause update = queryFactory.update(qFaceSearch)
                .where(qFaceSearch.searchId.eq(searchId));
        if (StringUtils.hasText(msg)) {
            update.set(qFaceSearch.msg, msg);
        }
        if (longitude != null) {
            update.set(qFaceSearch.longitude, longitude);
        }
        if (latitude != null) {
            update.set(qFaceSearch.latitude, latitude);
        }
        if (StringUtils.hasText(place)) {
            update.set(qFaceSearch.place, place);
        }
        return update.isEmpty() ? 0L : update.execute();
    }

    public static class FaceSearchParam {
        public String msg_type;
        public String searchId;
        public String userId;
        public String type;
        public List<ImageData> content = new ArrayList<>(2);
    }

    public static class ImageData {
        public String image;
        public String md5;
    }

    /**
     * 人脸识别搜索结果
     */
    public static class FaceSearchResult {
        public String msg_type;
        public String errmsg;
        public String errcode;
        public List<Double> score;
        public List<String> id;
        public List<Double> baby_score;
        public List<String> baby_id;
        public List<Double> news_score;
        public List<String> news_id;
    }

    /**
     * 返回给APP的搜索结果
     */
    /*public static class AppSearchResult {
        public String searchId;
        public List<FaceSearchUser> users;
    }*/
    public AppSearchResult faceSearchUser(
            String myUserId, String pathName,
            Double longitude, Double latitude, String place) {
        if (!StringUtils.hasText(myUserId)) {
            throw new AppException("进行搜索的用户不能为空！");
        }
        if (!StringUtils.hasText(pathName)) {
            throw new AppException("搜索的图片地址不能为空！");
        }

        // 保存搜索记录
        FaceSearch record = new FaceSearch();
        String recordId = GlobalIdWorker.nextString();
        record.setSearchId(recordId);
        record.setFacePicUrl(pathName);
        record.setUserId(myUserId);
        record.setLongitude(longitude);
        record.setLatitude(latitude);
        record.setPlace(place);
        record.setSearchTime(new Date());
        queryFactory.insert(qFaceSearch)
                .populate(record)
                .execute();

        logger.info("保存搜索记录成功!recordId：" + recordId);

        // 准备搜索参数并搜索相似用户
        FaceSearchParam param = new FaceSearchParam();
        param.msg_type = "findSimilarityImg";
        param.searchId = record.getSearchId();
        param.type = "1"; // 0.搜索明星，1.搜索用户
        param.userId = myUserId;
        try {
            byte[] bytes = OkHttpUtils.get(pathName).callForBytes();
            ImageData imageData = new ImageData();
            imageData.image = Base64.getEncoder().encodeToString(bytes);
            imageData.md5 = DigestUtils.md5DigestAsHex(bytes);
            param.content.add(imageData);
        } catch (Exception e) {
            throw new AppException("获取图片数据失败。");
        }

        String data = GsonUtils.toJson(param);
        //logger.info("data:" + data);
        Long start = System.currentTimeMillis();
        FaceSearchResult result = OkHttpUtils.formPost(appConfig.getFaceRecognitionUrl())
                .param("data", data)
                .callForObject(FaceSearchResult.class);


        Long end = System.currentTimeMillis();
        logger.info("time:" + (end - start));
        logger.info("result:" + JsonConverter.toJson(result));
        logger.info("result.errmsg:" + result.errmsg);
        logger.info("result.errcode:" + result.errcode);

        if ("0".equals(result.errcode)) {
            AppSearchResult appSearchResult = new AppSearchResult();
            appSearchResult.setSearchId(recordId);
            return appSearchResult;
        }

        if (((result.id == null) || (result.id.size() == 0))
                && ((result.baby_id == null) || (0 == result.baby_id.size()))) {
            AppSearchResult appSearchResult = new AppSearchResult();
            appSearchResult.setSearchId(recordId);
            return appSearchResult;
        }

        List<FaceSearchUser> searchUsers = new ArrayList<>();
        List<FaceSearchChild> searchChildren = new ArrayList<>();
        List<FaceSearchNews> searchNews = new ArrayList<>();
        AppSearchResult appResult = new AppSearchResult();
        appResult.searchId = record.getSearchId();

        if ((result.id != null) && (result.id.size() > 0)) {
            //搜到了用户
            List<User> userList = userFeignClient.getUsers(
                    result.id.toArray(new String[result.id.size()])).getData();

            // 查找黑名单ID
            List<String> blackIds = friendsService.findFriendIds(myUserId, 0);

            // 转换成搜索结果用户
            for (User user : userList) {
                // 忽略不允许人脸搜索自己的人
                if (user.getAllowCoolFaceMe() == 0) {
                    continue;
                }
                // 忽略黑名单的人
                if (blackIds.contains(user.getUserId())) {
                    continue;
                }
                FaceSearchUser searchUser = new FaceSearchUser();
                BeanUtils.copyProperties(user, searchUser);
                searchUser.setSimilarRate(result.score.get(result.id.indexOf(user.getUserId())));
                // 处理好友关系
                FriendsService.HandleResult handleResult = friendsService.getFriendHandle(myUserId, user.getUserId());
                searchUser.setFriendHandle(handleResult.handleFlag);
                searchUser.setConfirmId(handleResult.confirmId);
                searchUsers.add(searchUser);
            }

            //根据相似度降序排序
            searchUsers.sort(Comparator.comparing(FaceSearchUser::getSimilarRate).reversed());
            appResult.users = searchUsers;
        }

        //2018-02-03需求：搜索结果去除失踪儿童
//        if((result.baby_id != null) && (result.baby_id.size() > 0)){
//            //搜到了失踪儿童
//            String[] childrenIds = result.baby_id.toArray(new String[result.baby_id.size()]);
//            List<LostChild> listChildren = lostChildService.findChildren(childrenIds);
//            for(LostChild child : listChildren){
//                FaceSearchChild faceSearchChild = new FaceSearchChild();
//                BeanUtils.copyProperties(child, faceSearchChild);
//                faceSearchChild.setSimilarRate(result.baby_score.get(result.baby_id.indexOf(child.getId())));
//                searchChildren.add(faceSearchChild);
//            }
//            searchChildren.sort(Comparator.comparing(FaceSearchChild::getSimilarRate).reversed());
////            appResult.lostChildren = searchChildren;
//        }


        if ((result.news_id != null) && (result.news_id.size() > 0)) {
            logger.info("人脸搜索新闻的长度："+result.news_id.size());
            //搜到了新闻
            //刘杰那边给“1213223432userId”这种格式，哔了狗
            List<Long> newsIdLong = new ArrayList<>();
            for (String stringId: result.news_id){
                try {
                    Long longId =Long.valueOf(stringId);
                    newsIdLong.add(longId);
                }catch (Exception e){
                    logger.info(e.getMessage());
                }
            }
            if (newsIdLong.size()==0){
                logger.info("人脸搜索新闻 id 转换成long型 大小为空：");
                return appResult;
            }

            ResultMessage< List<CmfPostsListVo>> resultMessage = newsClient.getNewsByIds(newsIdLong);
            if (resultMessage.isSuccess()){
                List<CmfPostsListVo> newsList = resultMessage.getData();
                if (newsList == null){
                    logger.info("根据long型新闻id，搜索的平台结果 为空");
                    return appResult;
                }
                FaceSearchNews faceSearchNews ;
                for (CmfPostsListVo news:newsList){
                    faceSearchNews = new FaceSearchNews();
                    BeanUtils.copyProperties(news,faceSearchNews);

                    try {
                        faceSearchNews.setSimilarRate(result.news_score.get(result.news_id.indexOf(news.getId())));
                    }catch (Exception e){
                        faceSearchNews.setSimilarRate(Double.valueOf(0));
                    }
//
                    //这个字段太长，前端也不需要，暂时设为空
                    faceSearchNews.setPostContent("");
                    searchNews.add(faceSearchNews);
                }
                searchNews.sort(Comparator.comparing(FaceSearchNews::getSimilarRate).reversed());
                appResult.news = searchNews;
            }
        }else {
            logger.info("人脸搜索新闻 的长度 为空：");
        }

        return appResult;
    }

    /**
     * 查询谁搜索过我
     */
    public AppSearchResult whoCoolfaceMe(String myUserId) {
        // 准备搜索参数并搜索相似用户
        FaceSearchParam param = new FaceSearchParam();
        param.msg_type = "searchMe";
        param.type = "1"; // 0.搜索明星，1.搜索用户
        param.userId = myUserId;

        String data = GsonUtils.toJson(param);
        FaceSearchResult result = new FaceSearchResult();
        try {
            OkHttpUtils.FormPostBuilder formPostBuilder = OkHttpUtils.formPost(appConfig.getFaceRecognitionUrl())
                    .param("data", data);

            String jsonString = formPostBuilder.callForString();
            logger.info("jsonString:" + jsonString);

            result = formPostBuilder.callForObject(FaceSearchResult.class);
        } catch (Exception e) {
            logger.info(e.getMessage());
            return new AppSearchResult();
        }

        if (result == null || !"1".equals(result.errcode)) {
            return null;
        }
        if (result.id == null || result.id.size() == 0) {
            return null;
        }

        logger.info("result.id.size():" + result.id.size());
        logger.info("result.score.size():" + result.score.size());

        // 把返回的searchId转换成用户ID
        List<FaceSearch> listFaceSearch = queryFactory.select(qFaceSearch)
                .from(qFaceSearch)
                .where(qFaceSearch.searchId.in(result.id))
                .fetch();

        // 查找黑名单ID
        List<String> blackIds = friendsService.findFriendIds(myUserId, 0);

        // 转换成搜索结果用户
        List<FaceSearchUser> searchUsers = new ArrayList<FaceSearchUser>(listFaceSearch.size());
        if ((null != listFaceSearch)
                && (0 < listFaceSearch.size())) {
            for (FaceSearch faceSearch : listFaceSearch) {
                // 忽略黑名单的人
                if ((null != blackIds) && (blackIds.contains(faceSearch.getUserId()))) {
                    continue;
                }
                User user = userFeignClient.getUser(faceSearch.getUserId()).getData();

                FaceSearchUser searchUser = new FaceSearchUser();
                BeanUtils.copyProperties(user, searchUser);
                searchUser.setSimilarRate(result.score.get(result.id.indexOf(faceSearch.getSearchId())));
                searchUser.setFacePicUrl(faceSearch.getFacePicUrl());
                searchUser.setMsg(faceSearch.getMsg());
                // 处理好友关系
                FriendsService.HandleResult handleResult = friendsService.getFriendHandle(myUserId, user.getUserId());
                searchUser.setFriendHandle(handleResult.handleFlag);
                searchUser.setConfirmId(handleResult.confirmId);
                searchUser.setSearchTime(faceSearch.getSearchTime());
                searchUser.setProvince(user.getProvince());
                searchUser.setCity(user.getCity());
                searchUsers.add(searchUser);
            }
        }

        //根据相似度降序排序
        if (0 < searchUsers.size()) {
            searchUsers.sort(Comparator.comparing(FaceSearchUser::getSimilarRate).reversed());
        }

        AppSearchResult appResult = new AppSearchResult();
        appResult.users = searchUsers;
        return appResult;
    }
}
